{$APPTYPE CONSOLE}

program server_with_port_listen;

uses
  SysUtils,
  funcs,
  WinSock,
  EiDelphi,
  ErlDelphi;

const
  hostname = 'aiming2k';
  alivename = 'dnode';
  cookie = '123-456';
  nodename = alivename + '@' + hostname;

var
  port: Integer = 3456;
  er_conn : ErlConnect = (
    ipadr: (127,0,0,1);
    nodename: nodename
  );
  ipaddr : TInAddr = (S_un_b: (s_b1: 127; s_b2: 0; s_b3:0; s_b4: 1));

var
  fd : Integer;
  listen: Integer;

function my_listen(port: Integer): Integer;
var
  addr : sockaddr_in;
  on_  : integer;
begin
  Result := socket(AF_INET, SOCK_STREAM, 0);
  if Result<0 then
    Result := -1
  else
  begin
    on_ := 1;
    setsockopt(Result, SOL_SOCKET, SO_REUSEADDR, @on_, sizeof(on_));
    fillchar(addr, sizeof(sockaddr_in), 0);
    addr.sin_family := AF_INET;
    addr.sin_port := htons(port);
    addr.sin_addr.s_addr := htonl(INADDR_ANY);

    if bind(Result, addr, sizeof(addr)) < 0 then
      Result := -1
    else
      WinSock.listen(Result, 5);
  end;
end;


procedure alert_error(err: String='');
begin
  writeln(format('>>> Error(%d): %s', [erl_errno, err]));
  readln;
  halt(0);
end;

var
  buf : AnsiString;
  msg : ErlMessage;
  pbuf: PAnsiChar;
  pmsg: ErlMessage_p;

  got : Integer;
  fromp,tuplep,fnp,argp,resp : PETERM;

  func_name: String;
  func_value: Integer;

  r : Integer;
begin
  erl_init;

  r := erl_connect_xinit(hostname, alivename, nodename, @ipaddr, cookie, 1);
  if r = ERL_ERROR then alert_error('connect init...');

  listen := my_listen(port);
  r := erl_publish(port);
  if r = ERL_ERROR then alert_error('publish ...');
  fd := erl_accept(listen, @er_conn);
  if fd = ERL_ERROR then alert_error('accept init...');

  writeln('d-node init as server... ');
  SetLength(buf, 1024);
  fillchar(msg, sizeof(msg), 0);
  pmsg := @msg;
  while True do
  try
    got := erl_receive_msg(fd, PAnsiChar(buf), Length(buf), pmsg);
    case got of
      ERL_TICK : begin writeln('tick...'); continue; end;
      ERL_ERROR: alert_error('receive_msg...');
    else    // ERL MSG with a Emsg struct
      case msg.type_ of
        EiDelphi.ERL_REG_SEND:
        begin
          writeln('got messages!');

          // get call info.
          fromp := erl_element(2, msg.msg);    // { from, {fn, args}}
          tuplep := erl_element(3, msg.msg);
          fnp := erl_element(1, tuplep);       // fn
          argp := erl_element(2, tuplep);      // args

          // analies
          func_name := String(fnp.aval.a);
          func_value := 0;
          if func_name = 'foo' then
          begin
            func_value := foo(argp.ival.i);
          end
          else if func_name = 'bar' then
          begin
            func_value := bar(argp.ival.i);
          end;
          // more ..

          // return result
          resp := erl_format('{~a, ~i, ~s}', [alivename, func_value, 'hello']);
          erl_send(fd, fromp, resp);
          writeln('Send ...');

          // clear
          erl_free_term(msg.from);
          erl_free_term(msg.msg);
          erl_free_term(fromp);
          erl_free_term(tuplep);
          erl_free_term(fnp);
          erl_free_term(argp);
          erl_free_term(resp);
        end;
      else // ERL_SEND, ERL_LINK, ERL_UNLINK and ERL_EXIT
        writeln('unknow msg type:', msg.type_ );
      end;
    end;
  except
    on e:exception do
      writeln('>>> Error: ', e.Message);
  end;

readln;
end.
